#pragma once

#include "buffer.hpp"
#include <mutex>
#include <functional>
#include <condition_variable>
#include <memory>
#include <thread>
#include <atomic>

namespace log
{
  using Fanctor = std::function<void(Buffer &)>;
  enum class Asynctype
  {
    ASYNC_SAFE,  // 安全状态，如果缓冲区满了，可能导致资源耗尽,阻塞
    ASUNC_UNSAFE // 不考虑资源耗尽，无线扩容，用于测试
  };

  class AsyncLooper
  {
  public:
    using ptr = std::shared_ptr<AsyncLooper>;
    AsyncLooper(const Fanctor &callback, Asynctype loop_type = Asynctype::ASYNC_SAFE)
        : _stop(false),
          _thread(std::thread(&AsyncLooper::threadEntry, this)),
          _callback(callback), _looper_type(loop_type)
    {
    }

    ~AsyncLooper()
    {
      stop();
    }

    void stop()
    {
      _stop = true;
      _cond_con.notify_all(); // 将消费者唤醒
      _thread.join();         // 等待工作线程的退出
    }

    void push(const char *data, size_t len)
    {
      std::unique_lock<std::mutex> lock(_mutex);

      // 如果缓冲区剩余的空间大小大于数据的长度，则可以插入数据
      if (_looper_type == Asynctype::ASYNC_SAFE)
        _cond_pro.wait(lock, [&]()
                       { return _pro_buf.writeAbleSize() >= len; });

      _pro_buf.push(data, len);
      // 唤醒消费者对缓冲区中的数据进行处理
      _cond_con.notify_one();
    }

    void threadEntry() // 线程入口函数
    {
      while (!_stop)
      {
        {
          // 判断生产缓冲区是否没有数据，有就交换，没有就阻塞
          std::unique_lock<std::mutex> lock(_mutex);
          // 如果是退出前被唤醒或者是有数据被唤醒，则返回真，继续向下运行
          _cond_con.wait(lock, [&]()
                         { return _stop || !_pro_buf.empty(); });

          //退出标志，并且生产缓冲区没有数据了，再退出，不然可能出现缓冲区还有数据，但是已经退出的情况
          if(_stop && _pro_buf.empty()) break;

          _con_buf.swap(_pro_buf);
          if (_looper_type == Asynctype::ASYNC_SAFE)
            _cond_pro.notify_all(); // 唤醒所有的生产者
        }
        // 被唤醒后，对消费者缓冲区的数据进行处理
        _callback(_con_buf);
        // 初始化消费者缓冲区
        _con_buf.reset();
      }
    }

  private:
    Asynctype _looper_type;
    std::atomic<bool> _stop; // 其他线程也可能进行判断，所以设置为原子的
    Buffer _pro_buf;         // 生产缓冲区
    Buffer _con_buf;         // 消费缓冲区
    std::mutex _mutex;       // 互斥锁
    std::condition_variable _cond_pro;
    std::condition_variable _cond_con;
    Fanctor _callback;   // 回调函数 由异步工作器使用者来传入
    std::thread _thread; // 异步工作器的对应的专门线程
  };

}
